#define _GNU_SOURCE
#include <sched.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <immintrin.h>

#define errExit(msg) do{ perror(msg); exit(EXIT_FAILURE); \
										}while(0)

int main(int argc, char **argv)
{
	/* process control signal */
	int pipefd_1[2];
	int pipefd_2[2];
	pid_t cpid;
	/* thread affinity signal */
	cpu_set_t set;
	int cpu_num = 0; //on the same cpus and on the same core
	
	/* both process common used */
	char buf;
	char write_buf[2] = { 'a', 'b' };
	unsigned long loops;

	uint64_t avg = 0;

	if(argc != 2){
				fprintf(stderr, "Usage: %s loops\n",
												argv[0]);
				exit(EXIT_FAILURE);
	}
	//get para from command line
	loops = atoi(argv[1]);
	// clear set 
	CPU_ZERO(&set);

	/* create 2 pipe for communication between 2 process */
	if(pipe(pipefd_1) == -1){
			perror("pipe_1");
			exit(EXIT_FAILURE);
	}

	if(pipe(pipefd_2) == -1){
			perror("pipe_2");
			exit(EXIT_FAILURE);
	}

	/* use fork to create 2 process */

	cpid = fork();
	if(cpid == -1){
		perror("fork");
		exit(EXIT_FAILURE);
	}

	if(cpid == 0){ // child process
		printf("Child_process(pid):%d \n",(int)getpid());

		CPU_SET(cpu_num, &set);
		if(sched_setaffinity(getpid(), sizeof(set), &set) == -1)
				errExit("sched_setaffinity");

		/* you need read first and then continue to loop */
		read(pipefd_1[0], &buf, 1);

		for(int i = 0; i < loops; i++){
				//printf("child:read buf:%c loops_i: %d \n", buf, i);
				write(pipefd_2[1], &write_buf[1], 1);
				/* measure time */
				read(pipefd_1[0], &buf, 1); //blocked
				//printf("child:read buf:%c loops_i: %d \n", buf, i);
		}

		exit(0);

	}else{			// parent process
		//printf("Parent_process(pid):%d \n",(int)getpid());
		CPU_SET(cpu_num, &set);
		if(sched_setaffinity(getpid(), sizeof(set), &set) == -1)
				errExit("sched_setaffinity");
		for(int i = 0; i < loops; i++){
				//printf("parent: goes into there\n");
				write(pipefd_1[1], &write_buf[0], 1);
				/* measure time */
				const uint64_t st = _rdtsc();
				read(pipefd_2[0], &buf, 1); //blocked
				const uint64_t et = _rdtsc() -st;
				avg += et;
				//printf("parent:read buf:%c loops_i: %d \n", buf, i);
		}

		/* statistics cosumption */
		printf("Loops: %ld \n", loops);
		printf("Avg Ticks: %ld \n", avg);
		printf("Ticks Per Switch: %ld \n", avg / loops);

	}

	return 0;
}
